package pl.edu.pw.polygen.modeler.server.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

import pl.edu.pw.polygen.modeler.server.xml.Edge;
import pl.edu.pw.polygen.modeler.server.xml.MarshallService;
import pl.edu.pw.polygen.modeler.server.xml.PolygenData;
import pl.edu.pw.polygen.modeler.server.xml.Vertex;

public class ConverterService {
	public static PolygenData convertToPolygenData(File polyFile){
		PolygenData polygenData = new PolygenData();
		List<Vertex> vertexes = new ArrayList<Vertex>();
		int numOfVertices = 0;
		int dimension = 0;
		int attributes = 0;
		int numOfBoundary = 0;
		try {
			Scanner scanner = new Scanner(polyFile);
			scanner.useLocale(Locale.US);
			numOfVertices = scanner.nextInt();
			dimension = scanner.nextInt();
			attributes = scanner.nextInt();
			numOfBoundary = scanner.nextInt();
			System.out.println(numOfVertices + " " + dimension + " " + attributes + " " + numOfBoundary);
			
			for(int i=0;i<numOfVertices;i++){
				Vertex v = new Vertex();
				v.setId(scanner.nextInt());
				v.setX(scanner.nextDouble());
				v.setY(scanner.nextDouble());
				v.setBoundary(scanner.nextInt());
				v.setType("NORMAL_VERTEX");
				vertexes.add(v);
			}
			
			int numOfSegments = 0;
			int numOfSegmentBoundary = 0;
			numOfSegments = scanner.nextInt();
			numOfSegmentBoundary = scanner.nextInt();
			for(int i=0;i<numOfSegments;i++){
				Edge edge = new Edge();
				int segmentId = scanner.nextInt();
				int beginVertex = scanner.nextInt();
				int endVertex = scanner.nextInt();
				int boundary = scanner.nextInt();
				
				edge.setBegin(beginVertex);
				edge.setEnd(endVertex);
				edge.setType("SEGMENT");
				edge.setBoundary(boundary);
				
				vertexes.get(beginVertex - 1).addEdge(edge);
			}
			//wczytanie holes
			int numOfHoles = scanner.nextInt();
			for(int i = 0;i<numOfHoles;i++){
				Vertex v  = new Vertex();
				v.setType("HOLE_VERTEX");
				v.setId(scanner.nextInt());
				v.setX(scanner.nextDouble());
				v.setY(scanner.nextDouble());
				vertexes.add(v);
			}
			
			//wczytanie subdomains
			int numOfDomains = scanner.nextInt();
			for(int i = 0;i<numOfDomains;i++){
				Vertex v  = new Vertex();
				v.setType("SUBDOMAIN_VERTEX");
				v.setId(scanner.nextInt());
				v.setX(scanner.nextDouble());
				v.setY(scanner.nextDouble());
				v.setValue(scanner.nextDouble());
				vertexes.add(v);
			}
			polygenData.setVertexes(vertexes);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return polygenData;
	}
	
	public static File convertToPoly(String data, File polyFile){
		try {
			MarshallService marshallService = new MarshallService();
			PolygenData polygenData = marshallService.unmarshall(data);
			FileWriter fileWriter = new FileWriter(polyFile);
			BufferedWriter writer = new BufferedWriter(fileWriter);
			convertVertexes(polygenData, writer);
			writer.close();
			fileWriter.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return polyFile;
	}
	
	private static void convertVertexes(PolygenData polygenData,BufferedWriter writer){
		int dimension = 2;
		int numOfAttributes = 2;
		int boundaryMarkers = 1;
		Map<Integer, Integer> vertexMap = new HashMap<Integer, Integer>();
		try {
			
			//STAGE 1 load NORMAL_VERTEX
			List<Vertex> vertexes = polygenData.getVertexes("NORMAL_VERTEX");
			
			//STAGE 2 load Vertex from splain
			List<Edge> edges = polygenData.getEdges();
			
			List<Edge> splains = polygenData.getSplains();
			int lastId = polygenData.getLastId();
			for(Edge edge : splains){
				Vertex begin = polygenData.getVertexById(edge.getBegin());
				Vertex end = polygenData.getVertexById(edge.getEnd());
				Vertex controlA = polygenData.getVertexById(edge.getControlA());
				Vertex controlB = polygenData.getVertexById(edge.getControlB());
				double step = edge.getStep();
				
				Edge splainEdge = new Edge();
				splainEdge.setBegin(begin.getId());
				for (double t = step; t < 1-step; t += step) {
					lastId++;
					double x = computeBezierX(t, begin.getX(), controlA.getX(), controlB.getX(), end.getX());
					double y = computeBezierY(t, begin.getY(), controlA.getY(), controlB.getY(), end.getY());
					Vertex vertex = new Vertex();
					vertex.setX(x);
					vertex.setY(y);
					vertex.setId(lastId);
					
					
					vertexes.add(vertex);
					splainEdge.setEnd(vertex.getId());
					edges.add(splainEdge);
					splainEdge = new Edge();
					splainEdge.setBegin(vertex.getId());
				}
				splainEdge.setEnd(end.getId());
				edges.add(splainEdge);
			}
			
			writer.write(vertexes.size() + " " + dimension + " " + numOfAttributes+ " " + boundaryMarkers + "\n");
			int index = 1;
			for(int i=0;i<vertexes.size();i++){
				Vertex vertex = vertexes.get(i);				
				vertexMap.put(vertex.getId(), index);
				writer.write(index + " " + vertex.getX() + " " + vertex.getY() + " " + vertex.getBoundary() + "\n");
				index++;
			}
			
			
			writer.write(edges.size() + " " + boundaryMarkers + "\n");
			int edgeIndex = 1;
			for(int i=0;i<edges.size();i++){
				Edge edge = edges.get(i);
				int begin = vertexMap.get(edge.getBegin());
				int end = vertexMap.get(edge.getEnd());
				writer.write(edgeIndex + " " + begin + " " + end + " " + edge.getBoundary() + "\n");
				edgeIndex++;
			}
			
			List<Vertex> holes = polygenData.getVertexes("HOLE_VERTEX");
			writer.write(holes.size() + "\n");
			int holeIndex = 1;
			for(int i=0;i<holes.size();i++){
				Vertex hole = holes.get(i);
				writer.write(holeIndex + " " + hole.getX() + " " + hole.getY() + "\n");
				holeIndex++;
			}
			
			List<Vertex> subdomains = polygenData.getVertexes("SUBDOMAIN_VERTEX");
			writer.write(subdomains.size() + "\n");
			int subdomainsIndex = 1;
			for(int i=0;i<subdomains.size();i++){
				Vertex subdomain = subdomains.get(i);
				writer.write(subdomainsIndex + " " + subdomain.getX() + " " + subdomain.getY() + " " + subdomain.getValue()+ "\n");
				subdomainsIndex++;
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public static PolygenData loadNodes(File nodeFile){
		PolygenData polygenData = new PolygenData();
		List<Vertex> vertexes = new ArrayList<Vertex>();
		Scanner scanner;
		try {
			scanner = new Scanner(nodeFile);
			scanner.useLocale(Locale.US);
			int numOfVertices = scanner.nextInt();
			int dimension = scanner.nextInt();
			int attributes = scanner.nextInt();
			int numOfBoundary = scanner.nextInt();
			System.out.println(numOfVertices + " " + dimension + " " + attributes + " " + numOfBoundary);
			for(int i=0;i<numOfVertices;i++){
				Vertex v = new Vertex();
				v.setId(scanner.nextInt());
				v.setX(scanner.nextDouble());
				v.setY(scanner.nextDouble());
				for(int j=0;j<attributes;j++){
					double attribute = scanner.nextDouble();
				}
				v.setBoundary(scanner.nextInt());
				v.setType("NORMAL_VERTEX");
				vertexes.add(v);
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		polygenData.setVertexes(vertexes);
		return polygenData;
	}
	
	public static PolygenData loadTriangles(PolygenData polygenData, File eleFile){
		Map<Integer, Vertex> vertexMap = new HashMap<Integer, Vertex>();
		try {
			List<Vertex> vertexes = polygenData.getVertexes("NORMAL_VERTEX");
			boolean [][] edgesMap = new boolean[vertexes.size()][vertexes.size()];
			for(int i=0;i<vertexes.size();i++){
				Vertex vertex = vertexes.get(i);
				int index = i + 1;
				vertexMap.put(index, vertex);
				vertex.getEdges().clear();
			}
			Scanner scanner = new Scanner(eleFile);
			scanner.useLocale(Locale.US);
			
			int numberOfTriangles = scanner.nextInt();
			int nodesPerTriangle = scanner.nextInt();
			int numOfAttributes = scanner.nextInt();
			
			for(int i = 0; i <numberOfTriangles;i++){
				int triangleId = scanner.nextInt();
				int [] nodes = new int[nodesPerTriangle];
				for(int j=0;j< nodesPerTriangle;j++){
					nodes[j] = scanner.nextInt();
				}
				
				for(int j=0;j<nodesPerTriangle;j++){
					Vertex begin = vertexMap.get(nodes[j]);
					for(int k=j+1;k < nodesPerTriangle;k++){
						Vertex end = vertexMap.get(nodes[k]);
						if(edgesMap[nodes[j]-1][nodes[k]-1]==false){
							Edge edge = new Edge();
							edge.setBegin(begin.getId());
							edge.setEnd(end.getId());
							edge.setType("SEGMENT");
							begin.addEdge(edge);
							edgesMap[nodes[j]-1][nodes[k]-1] = true;
							edgesMap[nodes[k]-1][nodes[j]-1] = true;
						}
					}
				}
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return polygenData;
	}
	
	
	/**
	 * Calculate x coordinate of indirect point.
	 * @param t parameter of Bezier formula
	 * @return x coordinate of indirect point
	 */
	private static double computeBezierX(double t,double ax, double bx, double cx, double dx){
		double result = ax * (1 - t) * (1 - t) * (1 - t) + 
						3 * bx * t * (1 - t) * (1 - t) + 
						3 * cx * t * t * (1 - t) + 
						dx * t * t * t;
		return result;
	}
	
	/**
	 * Calculate y coordinate of indirect point
	 * @param t parameter of Bezier formula
	 * @return y coordinate of indiect point
	 */
	private static double computeBezierY(double t,double ay, double by, double cy, double dy){
		double result = ay * (1 - t) * (1 - t) * (1 - t) + 
						3 * by * t * (1 - t) * (1 - t) + 
						3 * cy * t * t * (1 - t) + 
						dy * t * t * t;
		return result;
	}

	public static File convertToDiffpack(String data, File diffpackFile) {
		try {
			MarshallService marshallService = new MarshallService();
			PolygenData polygenData = marshallService.unmarshall(data);
			FileWriter fileWriter = new FileWriter(diffpackFile);
			BufferedWriter writer = new BufferedWriter(fileWriter);
			
			List<Vertex> vertexes = polygenData.getVertexes("NORMAL_VERTEX");
			List<Edge> edges = polygenData.getEdges();
			
			List<Edge> splains = polygenData.getSplains();
			int lastId = polygenData.getLastId();
			
			for(Edge edge : splains){
				Vertex begin = polygenData.getVertexById(edge.getBegin());
				Vertex end = polygenData.getVertexById(edge.getEnd());
				Vertex controlA = polygenData.getVertexById(edge.getControlA());
				Vertex controlB = polygenData.getVertexById(edge.getControlB());
				double step = edge.getStep();
				
				Edge splainEdge = new Edge();
				splainEdge.setBegin(begin.getId());
				for (double t = step; t < 1-step; t += step) {
					lastId++;
					double x = computeBezierX(t, begin.getX(), controlA.getX(), controlB.getX(), end.getX());
					double y = computeBezierY(t, begin.getY(), controlA.getY(), controlB.getY(), end.getY());
					Vertex vertex = new Vertex();
					vertex.setX(x);
					vertex.setY(y);
					vertex.setId(lastId);
					
					
					vertexes.add(vertex);
					splainEdge.setEnd(vertex.getId());
					edges.add(splainEdge);
					splainEdge = new Edge();
					splainEdge.setBegin(vertex.getId());
				}
				splainEdge.setEnd(end.getId());
				edges.add(splainEdge);
			}
			
			writeHeader(writer,vertexes.size(),edges.size());
			Set<Integer> boundaryIndicators = polygenData.getBoundaryIndicators();
			writer.write(boundaryIndicators.size()+" Boundary indicators: ");
			Iterator<Integer> iterator = boundaryIndicators.iterator();
			while(iterator.hasNext()){
				Integer boundary = iterator.next();
				writer.write(boundary+" ");
			}
			writer.write("\n");
			writer.write("\n");
			Map<Integer, Integer> vertexMap = writeVertexes(writer,vertexes);
			writeNodes(writer, edges, vertexMap);
			writer.flush();
			writer.close();
			fileWriter.close();
		}catch(Exception e){
			
		}
		return diffpackFile;
	}
	
	private static void writeNodes(BufferedWriter writer, List<Edge> edges, Map<Integer, Integer> vertexMap) {
		try {
			writer.write("Element types and connectivity\n");
			writer.write("the columns contain:\n");
			writer.write("- element number\n");
			writer.write("- element type\n");
			writer.write("- subdomain number\n");
			writer.write("- the global node numbers of the nodes in the element.\n");
			writer.write("#\n");
			int index = 1;
			for(Edge e : edges){
				writer.write("\t"+index+" ElmT3n2D 1 "+ vertexMap.get(e.getBegin())+" "+vertexMap.get(e.getEnd())+"\n" );
				index++;
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	private static Map<Integer,Integer> writeVertexes(BufferedWriter writer, List<Vertex> vertexes) {
		Map<Integer,Integer> vertexMap = new HashMap<Integer,Integer>();
		try {
			writer.write("Nodal coordinates and nodal boundary indicators,\n");
			writer.write("the columns contain:\n");
			writer.write("- node number\n");
			writer.write("- coordinates\n");
			writer.write("- no of boundary indicators that are set (ON)\n");
			writer.write("- the boundary indicators that are set (ON) if any.\n");
			writer.write("#\n");
			int index = 1;
			for(Vertex v : vertexes){
				vertexMap.put(v.getId(), index);
				writer.write("\t"+index+" ( "+ v.getX()+", "+v.getY()+" ) " );
				if(v.getBoundary()!=0){
					writer.write("[1] " + v.getBoundary()+"\n");
				}else{
					writer.write("[0]\n");
				}
				index++;
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return vertexMap;
	}

	private static void writeHeader(BufferedWriter writer, int numberOfVertex, int numberOfNodes){
		try {
			writer.write("Finite element mesh (GridFE):\n");
			writer.write("Number of space dim. = 2\n");
			writer.write("Number of elements   = " + numberOfNodes + "\n");
			writer.write("Number of nodes      = " + numberOfVertex + "\n");
			writer.write("All elements are of the same type : dpTRUE\n");
			writer.write("Max number of nodes in an element: 2\n");
			writer.write("Only one subdomain               : dpFALSE\n");
			writer.write("Lattice data                     ? 0\n");
			writer.write(" \n");
			writer.write(" \n");
			writer.write(" \n");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
							 
	}
	

}
